#include "stdafx.h"
#include "MyDb.h"
#include "MyChannel.h"
#include "ZObjectPool.h"
#include "MyConfig.h"
#include "MyServer.h"

static UA_StatusCode readTagData(UA_Server *server,const UA_NodeId *sessionId, void *sessionContext,const UA_NodeId *nodeId, void *nodeContext,
	UA_Boolean sourceTimeStamp,const UA_NumericRange *range, UA_DataValue *value);
static UA_StatusCode writeTagData(UA_Server *server,const UA_NodeId *sessionId, void *sessionContext,const UA_NodeId *nodeId, void *nodeContext,
	const UA_NumericRange *range, const UA_DataValue *data);
static const UA_NodeId baseDataVariableType_1 = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEDATAVARIABLETYPE}};
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CMyTag::CMyTag(CMyNode* pParent,ISLApp* pIApp)
{
	m_pParent = pParent;
	m_pIApp = pIApp;
	m_nUaId = -1;
	m_nTypeId = 0;

	m_pIDbTag = NULL;
	m_pIDsTag = NULL;
	m_nVT = 0;
	m_nAccess = 0;


	m_nQuality = 0;
	m_nValue64 = 0;
}

CMyTag::~CMyTag()
{

}

std::string CMyTag::GetName(int nFlag)
{
	std::string csTemp = BN_StringFormat("%s.%s",m_pParent->GetName(1).c_str(),m_csName.c_str());
	return csTemp;
}

void CMyTag::DealAsdu(void* pZObject)
{
	TZData* pZData = (TZData*)pZObject;
	m_tTime = pZData->tTime;
	m_nQuality = pZData->nQuality;
	switch (pZData->nVT)
	{
	case SVT_INT:
		{
			m_nValue = pZData->nValue;
		}
		break;
	case SVT_INT64:
		{
			m_nValue64 = pZData->nValue64;

		}
		break;
	case SVT_DOUBLE:
		{
			m_dbValue = pZData->dbValue;

		}
		break;
	case SVT_STRING:
		{
			m_csValue = pZData->csValue;
		}
		break;
	}
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CMyNode::CMyNode(CMyNode* pParent,ISLApp* pIApp)
{
	m_pParent = pParent;
	m_pIApp = pIApp;
	m_pIDbGroup = NULL;
	m_nUaId = -1;
}

CMyNode::~CMyNode()
{
	Clear();
}

void CMyNode::Clear()
{
	for(CMyTagList::iterator it = m_lstTags.begin(); it != m_lstTags.end(); it++)
	{
		CMyTag* pTag = (CMyTag*)*it;
		delete pTag;
	}
	m_lstTags.clear();

	for(CMyNodeList::iterator it = m_lstSubNodes.begin(); it != m_lstSubNodes.end(); it++)
	{
		CMyNode* pNode = (CMyNode*)*it;
		delete pNode;
	}
	m_lstSubNodes.clear();
}

std::string CMyNode::GetName(int nFlag)
{
	if(nFlag == 0)
		return m_csName;
	else
	{
		if(m_pParent == NULL)
			return m_csName;
		//if(m_pParent->m_pParent == NULL)
		//	return m_csName;
		std::string csTemp = BN_StringFormat("%s.%s",m_pParent->GetName(1).c_str(),m_csName.c_str());
		return csTemp;
	}
	return "";
}

void CMyNode::_LoopBuildUAObjects(UA_Server* pUaServer,CMyChannel* pMyChannel, CMyServer* pServer)
{
	ISLApp* pIApp = pMyChannel->m_pIApp;
	//if(m_pParent == NULL)
	//	return ;
	for(CMyTagList::iterator it = m_lstTags.begin(); it != m_lstTags.end(); it++)
	{
		CMyTag* pMyTag = (CMyTag*)*it;

		UA_VariableAttributes v_attr = UA_VariableAttributes_default;
		switch (pMyTag->m_nVT)
		{
		case SVT_INT:
			{
				v_attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
			}
			break;
		case SVT_INT64:
			{
				v_attr.dataType = UA_TYPES[UA_TYPES_INT64].typeId;
			}
			break;
		case SVT_DOUBLE:
			{
				v_attr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
			}
			break;
		case SVT_STRING:
			{
				v_attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
			}
			break;
		case SVT_BLOCK:
			{
				v_attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
			}
			break;
		default:
			{
				v_attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
			}
			break;
		}
		
		std::string csDataEncoding = pIApp->EncodingUTF8_FromGBK(pMyTag->m_csDesc);
		v_attr.description = UA_LOCALIZEDTEXT("", (char*)csDataEncoding.c_str());
		v_attr.displayName = UA_LOCALIZEDTEXT("", (char*)pMyTag->m_csName.c_str());
		v_attr.accessLevel = UA_ACCESSLEVELMASK_READ;
		if(pMyTag->m_nAccess > 0)
			v_attr.accessLevel |= UA_ACCESSLEVELMASK_WRITE;
		v_attr.valueRank = -1;

		//const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, (char*)pMyTag->m_csName.c_str());
		
		//UA_NodeId nodeId = UA_NODEID_STRING(0, (char*)pMyTag->m_csNamePath.c_str());
		//UA_NodeId parent_nodeId = UA_NODEID_STRING(0, UA_NS0ID_BASEDATAVARIABLETYPE);

		// The value supplied for the attribute is not of the same type as the attribute's value.
		UA_DataSource dateDataSource;
		dateDataSource.read = readTagData;
		dateDataSource.write = writeTagData;
		UA_StatusCode uaResult = 0;
		UA_NodeId nodeId;
		if(pMyChannel->m_pMyConfig->m_uFlag.nIndexKind == 0)
		{
			uaResult = 	UA_Server_addDataSourceVariableNode(pUaServer, 
												UA_NODEID_STRING(1,(char*)pMyTag->m_csNamePath.c_str()), 
												UA_NODEID_STRING(1, (char*)m_csNamePath.c_str()),
												UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 
												UA_QUALIFIEDNAME(1, (char*)pMyTag->m_csName.c_str()),
												UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), 
												v_attr,
												dateDataSource, 
												pMyTag, 
												&nodeId);

		}
		else
		{
			uaResult = 	UA_Server_addDataSourceVariableNode(pUaServer, 
												UA_NODEID_NUMERIC(1, pMyTag->m_nUaId), 
												UA_NODEID_NUMERIC(1, m_nUaId),
												UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 
												UA_QUALIFIEDNAME(1, (char*)pMyTag->m_csName.c_str()),
												UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), 
												v_attr,
												dateDataSource, 
												pMyTag, 
												&nodeId);

		}
		if (UA_StatusCode_isGood(uaResult)) {
			pServer->addDataSetField(pUaServer, pMyTag->m_csName.c_str(), nodeId);
		} else {

			std::string csErr = std::string("FAILED_DB:") + UA_StatusCode_name(uaResult) + " Tag:" + pMyTag->m_csName;
			pMyChannel->m_pIChannel->ShowCommMsg(0, 0, NULL, 0, csErr.c_str());

// 		UA_Server_addVariableNode(pUaServer, UA_NODEID_NUMERIC(1, pMyTag->m_nUaId), 
// 								UA_NODEID_NUMERIC(1, m_nUaId),
// 								UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 
// 								UA_QUALIFIEDNAME(1, (char*)pMyTag->m_csName.c_str()),
// 								UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), 
// 								v_attr, NULL, NULL);
		
		}
	}



	UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;
	for(CMyNodeList::iterator it = m_lstSubNodes.begin(); it != m_lstSubNodes.end(); it++)
	{
		CMyNode* pSubNode = (CMyNode*)*it;


		std::string csDataEncoding = pIApp->EncodingUTF8_FromGBK(pSubNode->m_csDesc);
		object_attr.description = UA_LOCALIZEDTEXT("", (char*)csDataEncoding.c_str());
		object_attr.displayName = UA_LOCALIZEDTEXT("", (char*)pSubNode->m_csName.c_str());

		if(pMyChannel->m_pMyConfig->m_uFlag.nIndexKind == 0)
		{
			UA_Server_addObjectNode(pUaServer, UA_NODEID_STRING(1, (char*)pSubNode->m_csNamePath.c_str()),
								UA_NODEID_STRING(1, (char*)pSubNode->m_pParent->m_csNamePath.c_str()), 
								UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
								UA_QUALIFIEDNAME(1, (char*)pSubNode->m_csName.c_str()),
								UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), 
								object_attr, NULL, NULL);
		}
		else
		{
			UA_Server_addObjectNode(pUaServer, UA_NODEID_NUMERIC(1, pSubNode->m_nUaId),
								UA_NODEID_NUMERIC(1, pSubNode->m_pParent->m_nUaId), 
								UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
								UA_QUALIFIEDNAME(1, (char*)pSubNode->m_csName.c_str()),
								UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), 
								object_attr, NULL, NULL);
		}

		pSubNode->_LoopBuildUAObjects(pUaServer,pMyChannel, pServer);
	}

}

//Datasource Example
static UA_INLINE UA_DateTime UA_DateTime_fromSymLinkTime(TSYM_TIME& tTime) 
{
	return ((INT64)tTime.nTime * UA_DATETIME_SEC) + (((INT64)tTime.nTimeMills * 1000) * UA_DATETIME_USEC) + UA_DATETIME_UNIX_EPOCH;
}

static UA_StatusCode readTagData(UA_Server *server,const UA_NodeId *sessionId, void *sessionContext,const UA_NodeId *nodeId, void *nodeContext,
	UA_Boolean sourceTimeStamp,const UA_NumericRange *range, UA_DataValue *value) 
{
	if(range != NULL) 
	{
		value->hasStatus = true;
		value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
		return UA_STATUSCODE_GOOD;
	}
	if(nodeContext == NULL)
		return UA_STATUSCODE_GOOD;
	CMyTag* pMyTag = (CMyTag*)nodeContext;

	switch (pMyTag->m_nVT)
	{
	case SVT_INT:
		{
			UA_Int32 nValue = pMyTag->m_nValue;
			UA_Variant_setScalarCopy(&value->value, &nValue, &UA_TYPES[UA_TYPES_INT32]);
		}
		break;
	case SVT_INT64:
		{
			UA_Int64 nValue = pMyTag->m_nValue64;
			UA_Variant_setScalarCopy(&value->value, &nValue, &UA_TYPES[UA_TYPES_INT64]);
		}
		break;
	case SVT_DOUBLE:
		{
			UA_Double dbValue = pMyTag->m_dbValue;
			UA_Variant_setScalarCopy(&value->value, &dbValue, &UA_TYPES[UA_TYPES_DOUBLE]);
		}
		break;
	case SVT_STRING:
		{
			std::string csDataEncoding = pMyTag->m_pIApp->EncodingUTF8_FromGBK(pMyTag->m_csValue);
			UA_String usValue = UA_STRING((char*)csDataEncoding.c_str());
			UA_Variant_setScalarCopy(&value->value, &usValue, &UA_TYPES[UA_TYPES_STRING]);
			//UA_free
		}
		break;
	}

	//UA_DateTime currentTime = UA_DateTime_now();
	//UA_Variant_setScalarCopy(&value->value, &currentTime, &UA_TYPES[UA_TYPES_DATETIME]);
	value->hasValue = true;
	if(sourceTimeStamp) 
	{
		value->hasSourceTimestamp = true;
		value->sourceTimestamp = UA_DateTime_fromSymLinkTime(pMyTag->m_tTime);//(INT64)tTime.nTime * 1000 + tTime.nTimeMills;//currentTime;
	}
	return UA_STATUSCODE_GOOD;
}

static UA_StatusCode writeTagData(UA_Server *server,const UA_NodeId *sessionId, void *sessionContext,const UA_NodeId *nodeId, void *nodeContext,
	const UA_NumericRange *range, const UA_DataValue* pUaValue) 
{
	if(nodeContext == NULL)
		return UA_STATUSCODE_BADINTERNALERROR;
	CMyTag* pMyTag = (CMyTag*)nodeContext;
	IDsTag* pITag = pMyTag->m_pIDsTag;

	if(!pUaValue->hasValue)
		return UA_STATUSCODE_BADINTERNALERROR;

	TIO_WRITEDATA tIoWrite;
	tIoWrite.nKind = EIo_WriteData_Kind_FOD;
	tIoWrite.nWaitResponse = 0;
	std::string csWriteValue;
	switch (pUaValue->value.type->typeKind)
	{
	case UA_DATATYPEKIND_BOOLEAN:
		{
			UA_Boolean nValue = *(UA_Boolean*)pUaValue->value.data;
			tIoWrite.nVT = SVT_INT;
			tIoWrite.nValue = nValue;
			pITag->SetIoWrite(&tIoWrite);
		}
		break;
	case UA_DATATYPEKIND_SBYTE:
		{
			UA_SByte nValue = *(UA_SByte*)pUaValue->value.data;
			tIoWrite.nVT = SVT_INT;
			tIoWrite.nValue = nValue;
			pITag->SetIoWrite(&tIoWrite);
		}
		break;
	case UA_DATATYPEKIND_BYTE:
		{
			UA_Byte nValue = *(UA_Byte*)pUaValue->value.data;
			tIoWrite.nVT = SVT_INT;
			tIoWrite.nValue = nValue;
			pITag->SetIoWrite(&tIoWrite);
		}
		break;
	case UA_DATATYPEKIND_INT16:
		{
			UA_Int16 nValue = *(UA_Int16*)pUaValue->value.data;
			tIoWrite.nVT = SVT_INT;
			tIoWrite.nValue = nValue;
			pITag->SetIoWrite(&tIoWrite);
		}
		break;
	case UA_DATATYPEKIND_UINT16:
		{
			UA_UInt16 nValue = *(UA_UInt16*)pUaValue->value.data;
			tIoWrite.nVT = SVT_INT;
			tIoWrite.nValue = nValue;
			pITag->SetIoWrite(&tIoWrite);

		}
		break;
	case UA_DATATYPEKIND_INT32:
		{
			UA_Int32 nValue = *(UA_Int32*)pUaValue->value.data;
			tIoWrite.nVT = SVT_INT;
			tIoWrite.nValue = nValue;
			pITag->SetIoWrite(&tIoWrite);
		}
		break;
	case UA_DATATYPEKIND_UINT32:
		{
			UA_UInt32 nValue = *(UA_UInt32*)pUaValue->value.data;
			tIoWrite.nVT = SVT_INT;
			tIoWrite.nValue = nValue;
			pITag->SetIoWrite(&tIoWrite);
		}
		break;
	case UA_DATATYPEKIND_INT64:
		{
			UA_UInt64 nValue = *(UA_UInt64*)pUaValue->value.data;
			tIoWrite.nVT = SVT_INT;
			tIoWrite.nValue = nValue;
			pITag->SetIoWrite(&tIoWrite);
		}
		break;
	case UA_DATATYPEKIND_FLOAT:
		{
			UA_Float fValue = *(UA_Float*)pUaValue->value.data;
			tIoWrite.nVT = SVT_DOUBLE;
			tIoWrite.fValue = fValue;
			pITag->SetIoWrite(&tIoWrite);
		}
		break;
	case UA_DATATYPEKIND_DOUBLE:
		{
			UA_Double dbValue = *(UA_Double*)pUaValue->value.data;
			tIoWrite.nVT = SVT_DOUBLE;
			tIoWrite.fValue = dbValue;
			pITag->SetIoWrite(&tIoWrite);
		}
		break;
	case UA_DATATYPEKIND_STRING:
		{
			UA_String sValue = *(UA_String*)pUaValue->value.data;
			tIoWrite.nVT = SVT_STRING;
			char* pStringBuff = new char[sValue.length + 1];
			memcpy(pStringBuff,sValue.data,sValue.length);
			pStringBuff[sValue.length] = 0;
			tIoWrite.csValue = pStringBuff;
			delete []pStringBuff;
			pITag->SetIoWrite(&tIoWrite);
		}
		break;
	default:
		{
			return UA_STATUSCODE_BADINTERNALERROR;
		}
		break;
	}
	//UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"Changing the system time is not implemented");
	return UA_STATUSCODE_GOOD;
}

